﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Data;
using MoneyManagerNET.Model;
using System.Data.Metadata.Edm;
using System.Data.Objects;

namespace MoneyManagerNET.DAL.Repository.EF4
{
    public class EntityBus:IEntityBus 
    {
        static TraceSource trace = new TraceSource("storage");


        public EntityBus()
        {

            trace.TraceEvent(TraceEventType.Information, 0, "EntityFramework.EntityBus");

            EntityFrameworkContext = new SqlClientModelContainer    ();
            EntityFrameworkContext.CommandTimeout = 0;//SqlCe will crush without this
            EntityFrameworkContext.ObjectMaterialized += (s, e) => { trace.TraceInformation("materialized: {0}",e.Entity); };
            EntityFrameworkContext.SavingChanges += (s, e) => { trace.TraceInformation("saving changes: added:{0}   edited:{1}  deleted:{2}",
                EntityFrameworkContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Count(),
                EntityFrameworkContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified).Count(),
                EntityFrameworkContext.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted).Count()); };
        }





        SqlClientModelContainer EntityFrameworkContext = null;

        public IEnumerable<TEntity> Select<TEntity>(Func<TEntity, bool> where) where TEntity : class
        {
            trace.TraceInformation("EntityFramework Select {0}", typeof(TEntity).Name);

            var baseQuery=SelectListByType<TEntity>();
            
            var result=where == null ? baseQuery : baseQuery.Where(where);

            trace.TraceData(TraceEventType.Information, 0, result);

            return result;
        }




        private IEnumerable<TEntity> SelectListByType<TEntity>() where TEntity : class
        {
            System.Collections.IEnumerable e = null;
            
            if (typeof(TEntity) == typeof(Model.ITransaction)) e = EntityFrameworkContext.TransactionSet.Include("Category").Include("SubCategory").Include("Contragent");
            if (typeof(TEntity) == typeof(Model.ISubCategory)) e = EntityFrameworkContext.SubCategorySet;
            if (typeof(TEntity) == typeof(Model.ICategory)) e = EntityFrameworkContext.CategorySet.Include("SubCategoryList"); 
            if (typeof(TEntity) == typeof(Model.IStockInvestment)) e = EntityFrameworkContext.StockInvestmentSet;
            if (typeof(TEntity) == typeof(Model.IStockAccount)) e = EntityFrameworkContext.AccountSet.Where(acc=>acc is StockAccount);
            if (typeof(TEntity) == typeof(Model.ICurrency)) e = EntityFrameworkContext.CurrencySet;
            if (typeof(TEntity) == typeof(Model.IContragent)) e = EntityFrameworkContext.ContragentSet;
            if (typeof(TEntity) == typeof(Model.IBankAccount)) e = EntityFrameworkContext.AccountSet.Where(acc => acc is BankAccount);
            if (typeof(TEntity) == typeof(Model.IAccount)) e = EntityFrameworkContext.AccountSet;
            if (typeof(TEntity) == typeof(Model.IAsset)) e = EntityFrameworkContext.AssetSet;


            return e.Cast<TEntity>();
        }

        

        public TEntity New<TEntity>() where TEntity : class
        {
            trace.TraceInformation("New {0}",typeof(TEntity).Name);
            /*
             * this will not work because I query over Interface EF espects class
             * 
            TEntity entity = EntityFrameworkContext.CreateObject<TEntity>();
            
            var entitySetName=EntityFrameworkContext.GetEntitySet<TEntity>().Name;
            EntityFrameworkContext.AddObject(entitySetName, entity);
            return entity; 
             * 
            */

            object e = null;
            if (typeof(TEntity) == typeof(Model.ITransaction))
            {
                e = EntityFrameworkContext.CreateObject<Transaction>();
                EntityFrameworkContext.TransactionSet.AddObject((Transaction)e);
            }

            if (typeof(TEntity) == typeof(Model.ISubCategory))
            {
                e = EntityFrameworkContext.CreateObject<SubCategory>();
                EntityFrameworkContext.SubCategorySet.AddObject((SubCategory)e);
            }

            if (typeof(TEntity) == typeof(Model.ICategory))
            {
                e = EntityFrameworkContext.CreateObject<Category>();
                EntityFrameworkContext.CategorySet.AddObject((Category)e);
            }

            if (typeof(TEntity) == typeof(Model.IStockInvestment))
            {
                e = EntityFrameworkContext.CreateObject<StockInvestment>();
                EntityFrameworkContext.StockInvestmentSet.AddObject((StockInvestment)e);
            }


            if (typeof(TEntity) == typeof(Model.IStockAccount))
            {
                e = EntityFrameworkContext.CreateObject<StockAccount>();
                EntityFrameworkContext.AccountSet.AddObject((StockAccount)e);
            }

            if (typeof(TEntity) == typeof(Model.ICurrency))
            {
                e = EntityFrameworkContext.CreateObject<Currency>();
                EntityFrameworkContext.CurrencySet.AddObject((Currency)e);
            }

            if (typeof(TEntity) == typeof(Model.IContragent))
            {
                e = EntityFrameworkContext.CreateObject<Contragent>();
                EntityFrameworkContext.ContragentSet.AddObject((Contragent)e);
            }

            if (typeof(TEntity) == typeof(Model.IBankAccount))
            {
                e = EntityFrameworkContext.CreateObject<BankAccount>();
                EntityFrameworkContext.AccountSet.AddObject((BankAccount)e);
            }


            if (typeof(TEntity) == typeof(Model.IAccount))
            {
                e = EntityFrameworkContext.CreateObject<Account>();
                EntityFrameworkContext.AccountSet.AddObject((Account)e);
            }

            if (typeof(TEntity) == typeof(Model.IAsset))
            {
                e = EntityFrameworkContext.CreateObject<Asset>();
                EntityFrameworkContext.AssetSet.AddObject((Asset)e);
            }

            trace.TraceInformation("there are {0} new objects ",this.EntityFrameworkContext.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added));
            return (TEntity) e;
        }

        public void Delete<TEntity>(TEntity entity) where TEntity : class
        {
            trace.TraceInformation("EntityFramework Deleting object");
            EntityFrameworkContext.DeleteObject(entity);
        }

        public void SaveAll()
        {

            //weired but works
            //I get exception on first try to open connection
            //Handle is not initialized.  Void OpenStoreConnectionIf
            try { EntityFrameworkContext.Connection.Open(); }
            catch { }

            try
            {
                EntityFrameworkContext.SaveChanges();
            }
            catch (Exception exp) { 
                trace.TraceEvent(TraceEventType.Error,0,"ERROR: EntityFramework SaveAll :{0}",exp.Message);
            }
        }

        public void Reset()
        {
            throw new NotImplementedException();
        }
    }
}
